﻿using PLCS.Domain.Equipments;
using PLCS.Domain.Menus;
using PLCS.Domain.Shared.Tasks;
using PLCS.EquipmentService.Models;
using PLCS.Shared.Common.Constant;
using PLCS.Shared.Common.Extensions;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Volo.Abp.DependencyInjection;

namespace PLCS.EquipmentService.ParsersHelpers
{
    public class TaskParser : ITransientDependency
    {
        private readonly IInstructionRepository _instructionRepository;
        private readonly ITaskRepository _taskRepository;
        private readonly IStationRepository _stationRepository;
        private readonly IEquipmentRepository _equipmentRepository;

        public TaskParser(IInstructionRepository instructionRepository, ITaskRepository taskRepository, IStationRepository stationRepository, IEquipmentRepository equipmentRepository)
        {
            _instructionRepository = instructionRepository;
            _taskRepository = taskRepository;
            _stationRepository = stationRepository;
            _equipmentRepository = equipmentRepository;
        }

        /// <summary>
        /// 拼接一个指令号
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static int GetInstructionNo(int taskId, int index)
        {
            // 将第二个数字补全到 9 位数（前面加 0）
            string bStr = index.ToString().PadLeft(9 - taskId.ToString().Length, '0');

            // 拼接成字符串并补全到 9 位数（后面加 0）
            string s = $"{taskId}{bStr.PadRight(9 - taskId.ToString().Length, '0')}";

            // 将新的字符串转换为 int 类型
            return int.Parse(s);
        }

        /// <summary>
        /// 重新排序任务
        /// </summary>
        /// <param name="instructions"></param>
        /// <param name="task"></param>
        /// <returns></returns>
        public static List<Instruction> ReorderTaskNumbers(List<Instruction> instructions, TaskModel task)
        {
            for (int i = 0; i < instructions.Count; i++)
            {
                instructions[i].TaskId = task.Id;
                instructions[i].TrayType = task.TrayType;
                instructions[i].InstructionNo = GetInstructionNo(task.TaskId, i + 1);
            }
            return instructions;
        }

        #region 入库用

        /// <summary>
        /// 找到满足条件可以入库的货位
        /// </summary>
        /// <param name="kind"></param>
        /// <returns></returns>
        public async Task<Station?> FindAvailableStorageStation(TrayType trayType, List<Station>? stations = null)
        {
            // 1.满足是同一规格的货位或比当前规格大
            // 2.满足是空位的货位
            // 3.且没有任务的工位
            // 4.排序，优先是里面一排，其次是外面一排

            if (stations == null)
                stations = await _stationRepository.GetListAsync(true);

            var kindist = Enum.GetValues(typeof(TrayType)).Cast<int>().OrderBy(x => x);

            IEnumerable<Station> availableStations = null;

            var endPos = (await _taskRepository.GetListAsync()).GroupBy(x => x.EndPos).Select(x => x.Key);

            foreach (var kind in kindist)
            {
                if (kind < (int)trayType)
                    continue;

                availableStations = stations.Where(x => x.IsUse == YesNo.是 && x.IsLocked == YesNo.否)
              .Where(x => x.Kind == Enum.Parse(typeof(TrayType), kind.ToString()).ToString())
              .Where(x => x.HaveStatus == YesNo.否)
              .Where(x => !endPos.Contains(x.StaNum))
              .OrderBy(x =>
              {
                  var staNumParts = x.StaNum.Split('-');
                  var part = int.Parse(staNumParts[1]);

                  if (part == 1 || part == 4)
                  {
                      return 1; // 优先级1: 1排和4排（内层）
                  }
                  else
                  {
                      return 2; // 优先级2: 2排和3排（外层）
                  }
              })
             .ThenBy(x => int.Parse(x.StaNum.Split('-')[3]))
             .ThenBy(x => int.Parse(x.StaNum.Split('-')[2])); // 按照一列一列从小到大

                if (availableStations != null && availableStations.Any())
                    break;
            }

            //var a = availableStations.Select(x => x.StaName);
            return availableStations?.FirstOrDefault();
        }

        /// <summary>
        /// 找到满足条件可以入库的货位
        /// </summary>
        /// <param name="kind"></param>
        /// <returns></returns>
        public async Task<Station?> FindAvailableStorageStation(TrayType trayType, int materialSize, List<Station>? stations = null)
        {
            stations ??= await _stationRepository.GetListAsync(true);

            // 这边先写死，晚点找个地方配置一下超高高度
            int maxMaterialSize = GlobalModel.maxMaterialSize;

            IEnumerable<Station> availableStations = null;

            var endPos = (await _taskRepository.GetListAsync()).GroupBy(x => x.EndPos).Select(x => x.Key);

            for (int i = 1; i < maxMaterialSize; i++)
            {
                if (i < materialSize)
                    continue;

                // 1.过滤掉允许使用和未被锁定的
                // 2.选择托盘类型兼容的
                // 3.选择物料尺寸兼容的
                // 4.选择空位的
                // 5.过滤掉有任务到的地方
                availableStations = stations.Where(x => x.StationType == Domain.Shared.Stations.StationType.无光电货位)
                    .Where(x => x.IsUse == YesNo.是 && x.IsLocked == YesNo.否)
                    .Where(x => x.MultipleTrayType.Contains(((int)trayType).ToString()))
                    .Where(x => x.MultipleMaterialSize == i)
                    .Where(x => x.HaveStatus == YesNo.否)
                    .Where(x => !endPos.Contains(x.StaNum))
                    .OrderBy(x => int.Parse(x.StaNum.Split('-')[0]))
                    .ThenBy(x => int.Parse(x.StaNum.Split('-')[1]))
                    .ThenBy(x => int.Parse(x.StaNum.Split('-')[2]))
                    .ThenBy(x => int.Parse(x.StaNum.Split('-')[3]));

                if (availableStations != null && availableStations.Any())
                    break;
            }

            return availableStations?.FirstOrDefault();
        }

        #endregion 入库用

        #region 移库用，找到最近的一个满足条件可以移库的货位

        /// <summary>
        /// 找到最近的一个满足条件的相邻货位
        /// </summary>
        /// <param name="targetStation">目标货位</param>
        /// <param name="stations">所有货位列表</param>
        /// <returns>最近的一个满足条件的相邻货位</returns>
        public async Task<Station?> FindNearestEmptyStation(Station targetStation, List<Station> stations)
        {
            var parts = targetStation.StaNum.Split('-');
            int targetRoadWay = int.Parse(parts[0]);
            int targetRow = int.Parse(parts[1]);
            int targetColumn = int.Parse(parts[2]);
            int targetLayer = int.Parse(parts[3]);
            var materialSize = targetStation.MultipleMaterialSize;

            int offset = 1; // 偏移量，初始为1
            var allEndPosTasks = await _instructionRepository.GetListAsync();

            while (true)
            {
                if (offset > targetColumn)
                    return null;

                // 左侧货位内侧
                string leftInStation = GetStation(targetRoadWay, targetRow == 2 ? 1 : 4, targetColumn - offset, targetLayer);
                if (IsStationOK(leftInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == leftInStation);

                // 左侧货位外侧
                string leftStation = GetStation(targetRoadWay, targetRow, targetColumn - offset, targetLayer);
                if (IsStationOK(leftStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == leftStation);

                // 右侧货位内测
                string rightInStation = GetStation(targetRoadWay, targetRow == 2 ? 1 : 4, targetColumn + offset, targetLayer);
                if (IsStationOK(rightInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == rightInStation);

                // 右侧货位外侧
                string rightStation = GetStation(targetRoadWay, targetRow, targetColumn + offset, targetLayer);
                if (IsStationOK(rightStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == rightStation);

                // 上方货位内测
                string aboveInStation = GetStation(targetRoadWay, targetRow == 2 ? 1 : 4, targetColumn, targetLayer - offset);
                if (IsStationOK(aboveInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == aboveInStation);

                // 上方货位外侧
                string aboveStation = GetStation(targetRoadWay, targetRow, targetColumn, targetLayer - offset);
                if (IsStationOK(aboveStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == aboveStation);

                // 下方货位内测
                string belowInStation = GetStation(targetRoadWay, targetRow == 2 ? 1 : 4, targetColumn, targetLayer + offset);
                if (IsStationOK(belowInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == belowInStation);

                // 下方货位外侧
                string belowStation = GetStation(targetRoadWay, targetRow, targetColumn, targetLayer + offset);
                if (IsStationOK(belowStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == belowStation);

                // 对面左侧货位内测
                string oppositeLeftInStation = GetStation(targetRoadWay, targetRow == 2 ? 4 : 1, targetColumn - offset, targetLayer);
                if (IsStationOK(oppositeLeftInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeLeftInStation);

                // 对面左侧货位外侧
                string oppositeLeftStation = GetStation(targetRoadWay, targetRow == 2 ? 3 : 2, targetColumn - offset, targetLayer);
                if (IsStationOK(oppositeLeftStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeLeftStation);

                // 对面右侧货位内测
                string oppositeRightInStation = GetStation(targetRoadWay, targetRow == 2 ? 4 : 1, targetColumn + offset, targetLayer);
                if (IsStationOK(oppositeRightInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeRightInStation);

                // 对面右侧货位外侧
                string oppositeRightStation = GetStation(targetRoadWay, targetRow == 2 ? 3 : 2, targetColumn + offset, targetLayer);
                if (IsStationOK(oppositeRightStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeRightStation);

                // 对面上方货位内测
                string oppositeAboveInStation = GetStation(targetRoadWay, targetRow == 2 ? 4 : 1, targetColumn, targetLayer - offset);
                if (IsStationOK(oppositeAboveInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeAboveInStation);

                // 对面上方货位外侧
                string oppositeAboveStation = GetStation(targetRoadWay, targetRow == 2 ? 3 : 2, targetColumn, targetLayer - offset);
                if (IsStationOK(oppositeAboveStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeAboveStation);

                // 对面下方货位内测
                string oppositeBelowInStation = GetStation(targetRoadWay, targetRow == 2 ? 4 : 1, targetColumn, targetLayer + offset);
                if (IsStationOK(oppositeBelowInStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeBelowInStation);

                // 对面下方货位外侧
                string oppositeBelowStation = GetStation(targetRoadWay, targetRow == 2 ? 3 : 2, targetColumn, targetLayer + offset);
                if (IsStationOK(oppositeBelowStation, stations, materialSize, allEndPosTasks))
                    return stations.First(x => x.StaNum == oppositeBelowStation);

                offset++; // 偏移量递增
            }
        }

        /// <summary>
        /// 判断货位是否为空
        /// </summary>
        /// <param name="station">货位编号</param>
        /// <returns>是否为空</returns>
        public bool IsStationOK(string station, List<Station> stations, int materialSize, List<Instruction> allEndPosTasks)
        {
            //0.是否存在
            //1.需要判断是否满位
            //2.需要判断是否是同种规格
            var newStation = stations.FirstOrDefault(x => x.StaNum == station);

            if (newStation == null)
                return false;
            var newMaterialSize = newStation.MultipleMaterialSize;

            var endPosTasks = allEndPosTasks.Where(x => x.EndPos == newStation.StaNum);
            if (newStation.HaveStatus == YesNo.是 || endPosTasks.Any() || materialSize < newMaterialSize)
                return false;
            return true;
        }

        /// <summary>
        /// 根据行号和列号获取货位编号
        /// </summary>
        /// <param name="row">行号</param>
        /// <param name="column">列号</param>
        /// <returns>货位编号</returns>
        private string GetStation(int roadway, int row, int column, int layer)
        {
            return $"{roadway}-{row}-{column}-{layer}";
        }

        #endregion 移库用，找到最近的一个满足条件可以移库的货位

        #region 同一分类下找合适的工位

        /// <summary>
        /// 检查工位是否能进
        /// </summary>
        /// <param name="station"></param>
        /// <returns></returns>
        public async Task<bool> CheckStation(Station station, IEnumerable<TaskModel> tasks)
        {
            var instructions = await _instructionRepository.GetUnfinishedInstructionsAsync();
            // 如果工位的分类为空，那就不用检查了
            if (string.IsNullOrEmpty(station.Kind))
                return true;

            //if (station.StaStatus != DeviceStatus.空闲)
            //    return false;
            var endposTasks = tasks.Where(x => x.EndPos == station.StaNum && x.TaskStatus != Domain.Shared.Tasks.TaskStatus.未执行);

            // 如果是队列工位和栈工位，就判断是否托盘数量是否等于最高容量（也就是有没有满）
            if (station.StationType == Domain.Shared.Stations.StationType.栈工位 || station.StationType == Domain.Shared.Stations.StationType.队列工位)
            {
                // 判断目的位是这个工位的任务是否等于1并且是当前这个任务，不满足则返回false
                //if (!(endposTasks.Count() <= station.Capacity && endposTasks.Any(x => x.Id == task.Id)) && endposTasks.Any())
                if (endposTasks.Count() == station.Capacity && !instructions.Any(x => x.StartPos == station.StaNum))
                {
                    return false;
                }
                if (station.TrayCount == station.Capacity)
                {
                    return false;
                }
            }
            // 否则判断空满位即可
            else
            {
                // 判断目的位是这个工位的任务是否等于1并且是当前这个任务，不满足则返回false
                //if (!(endposTasks.Count() == 1 && endposTasks.First().Id == task.Id) && endposTasks.Any())
                if (endposTasks.Count() == 1 && !instructions.Any(x => x.StartPos == station.StaNum))
                    return false;
                if (station.HaveStatus == YesNo.是)
                    return false;
            }

            if (station.IsUse == YesNo.否)
                return false;

            return true;
        }

        /// <summary>
        /// 得到同一个分类下合适的工位
        /// </summary>
        /// <param name="stations"></param>
        /// <param name="station"></param>
        /// <returns></returns>
        public async Task<string> GetBestFitWorkstation(IEnumerable<Station> stations, Station endStation, IEnumerable<TaskModel> tasks, TaskModel task)
        {
            // 找到同一分类下所有的工位，并按优先级排序
            var workstationsOfCategory = stations.Where(x => x.Kind == endStation.Kind && x.StaNum != task.StartPos).OrderByDescending(x => x.Level);

            // 如果分类下只有一个工位，直接返回该工位的编号
            if (workstationsOfCategory.Count() == 1)
                return endStation.StaNum;

            // 遍历工位集合，检查每个工位是否满足任务的要求
            foreach (var workstation in workstationsOfCategory)
            {
                // 如果找到符合条件的工位，返回工位的编号
                if (await CheckStation(workstation, tasks))
                    return workstation.StaNum;
            }

            // 找到同一分组下目标位任务最少的一个
            var minCountWorkstation = tasks.GroupBy(x => x.EndPos)
                // 过滤出所有任务分组下包含这些分类的任务
                .Where(x => workstationsOfCategory.Select(x => x.StaNum).Contains(x.Key))
                // 按照每个分组中出现次数的升序排序
                .OrderBy(x => x.Count())
                // 如果次数都一样，就按照工位的优先级再排序
                .ThenByDescending(x => workstationsOfCategory.First(w => w.StaNum == x.Key).Level)
                .First()
                .Key;

            // 返回出现次数最少的工位编号
            return minCountWorkstation;
        }

        #endregion 同一分类下找合适的工位

        #region 双RGV避让算法

        public async Task<List<Instruction>> GenerateAvoidanceTasksAsync(Instruction instruction, int safeDistance)
        {
            var equipments = instruction.EquipmentNo.Split(ConstantChar.SplitChar);
            var equipment1 = await _equipmentRepository.GetByEquipmentNoAsync(equipments[0]);
            var equipment2 = await _equipmentRepository.GetByEquipmentNoAsync(equipments[1]);
            var startStation = await _stationRepository.GetStationByNumAsync(instruction.StartPos);
            var endStation = await _stationRepository.GetStationByNumAsync(instruction.EndPos);

            int equipment1Position = equipment1.CurrentPos; // 1号RGV当前位置
            int equipment2Position = equipment2.CurrentPos; // 2号RGV当前位置

            int taskStartPosition = startStation.Position; // 起始位坐标
            int taskEndPosition = endStation.Position; // 目的位坐标

            int maxTaskPosition = Math.Max(taskStartPosition, taskEndPosition); // 起始位和目标位的最大值
            int minTaskPosition = Math.Min(taskStartPosition, taskEndPosition); // 起始位和目标位的最小值

            // 1.确定需不需要启用两个RGV。
            if (equipment1Position < minTaskPosition && equipment1Position > maxTaskPosition)
            {
                if (equipment2Position < minTaskPosition && equipment1Position > maxTaskPosition && Math.Abs(equipment2Position - taskStartPosition) > Math.Abs(equipment1Position - taskStartPosition))
                {
                    instruction.EquipmentNo = equipment1.EquNum;
                }
                else
                {
                    instruction.EquipmentNo = equipment2.EquNum;
                }
                return new List<Instruction> { instruction };
            }
            else
            {
                if (equipment2Position < minTaskPosition && equipment1Position > maxTaskPosition)
                {
                    instruction.EquipmentNo = equipment1.EquNum;
                    return new List<Instruction> { instruction };
                }
            }

            // 2.确定主辅RGV
            int taskDistance = Math.Abs(taskEndPosition - taskStartPosition);

            var instructions = new List<Instruction>();

            int choseRgv1Position = (Math.Abs((equipment2Position - equipment1Position > 0 ? maxTaskPosition : minTaskPosition) - equipment2Position) + safeDistance)
             + (equipment1Position - taskStartPosition) + taskDistance;

            int choseRgv2Position = (Math.Abs((equipment1Position - equipment2Position > 0 ? maxTaskPosition : minTaskPosition) - equipment1Position) + safeDistance)
                + (equipment2Position - taskStartPosition) + taskDistance;

            if (choseRgv1Position > choseRgv2Position)
            {
                var moveStationPos = equipment1Position < equipment2Position ? minTaskPosition - safeDistance : maxTaskPosition + safeDistance;
                var moveInstruction = new Instruction(1, instruction.TaskId, equipment1.EquNum, InstructionType.单坐标行走);
                moveInstruction.SetMovePos(moveStationPos);
                instructions.Add(moveInstruction);
                instructions.Add(new Instruction(2, instruction.TaskId, equipment2.EquNum, InstructionType.取放货, startStation.StaNum, endStation.StaNum));
                return instructions;
            }
            else
            {
                var moveStationPos = equipment2Position < equipment1Position ? minTaskPosition - safeDistance : maxTaskPosition + safeDistance;
                var moveInstruction = new Instruction(1, instruction.TaskId, equipment2.EquNum, InstructionType.单坐标行走);
                moveInstruction.SetMovePos(moveStationPos);
                instructions.Add(moveInstruction);
                instructions.Add(new Instruction(2, instruction.TaskId, equipment1.EquNum, InstructionType.取放货, startStation.StaNum, endStation.StaNum));
                return instructions;
            }
        }

        #endregion 双RGV避让算法

        #region 多工位RGV任务解析

        public static List<List<Station>> CalculateAllPaths(List<Station> startPositions, List<Station> endPositions, int equipmentPos)
        {
            List<List<Station>> allPaths = new List<List<Station>>();
            List<Station> currentPath = new List<Station>();

            Dictionary<string, string> startEnd = new();
            for (int i = 0; i < startPositions.Count; i++)
            {
                startEnd.Add(endPositions[i].StaNum, startPositions[i].StaNum);
            }
            CalculatePathsRecursive(startPositions, endPositions, allPaths, currentPath, startEnd, new MinValueWrapper() { MinValue = int.MaxValue }, equipmentPos);

            return allPaths;
        }

        private static void CalculatePathsRecursive(List<Station> startPositions, List<Station> endPositions, List<List<Station>> allPaths, List<Station> currentPath, Dictionary<string, string> startEnd, MinValueWrapper minPosWrapper, int equipmentPos)
        {
            // 如果起始位和目的位都为空，则将当前路径添加到结果列表中
            if (startPositions.Count == 0 && endPositions.Count == 0)
            {
                var distanceList = currentPath.Select(x => x.Position).ToList();
                distanceList.Insert(0, equipmentPos);
                int res = Utility.CalculatedDistance(distanceList);
                if (res < minPosWrapper.MinValue)
                {
                    minPosWrapper.MinValue = res;
                    allPaths.Clear();
                    allPaths.Add(new List<Station>(currentPath));
                }
                return;
            }

            // 逐个遍历起始位
            int count = startPositions.Count;
            for (int i = 0; i < count; i++)
            {
                Station startPosition = startPositions[i];

                // 移除已经使用的起始位
                startPositions.RemoveAt(i);

                // 添加起始位到当前路径
                currentPath.Add(startPosition);

                // 递归计算剩余的路径
                CalculatePathsRecursive(startPositions, endPositions, allPaths, currentPath, startEnd, minPosWrapper, equipmentPos);

                // 回溯，移除起始位，并将它重新插入到列表的原来的位置
                currentPath.RemoveAt(currentPath.Count - 1);
                startPositions.Insert(i, startPosition);
            }

            // 逐个遍历目的位
            count = endPositions.Count;
            for (int i = 0; i < count; i++)
            {
                Station destinationPosition = endPositions[i];
                if (!currentPath.Select(x => x.StaNum).Contains(startEnd[destinationPosition.StaNum]))
                    continue;
                // 移除已经使用的目的位
                endPositions.RemoveAt(i);

                // 添加目的位到当前路径
                currentPath.Add(destinationPosition);

                // 递归计算剩余的路径
                CalculatePathsRecursive(startPositions, endPositions, allPaths, currentPath, startEnd, minPosWrapper, equipmentPos);

                // 回溯，移除目的位，并将它重新插入到列表的原来的位置
                currentPath.RemoveAt(currentPath.Count - 1);
                endPositions.Insert(i, destinationPosition);
            }
        }

        #endregion 多工位RGV任务解析
    }
}